I built a Visual Studio 2026 extension to bring back Blazor code-behind

Guided Tours Blazor component

If you write a lot of Blazor, you have probably developed an opinion about where your C# should live. Small components? Keep the @code block right there in the .razor file. But the moment a component grows a handful of fields, a couple of async lifecycle methods, and some event handlers, that single file starts to feel crowded — markup and logic fighting for the same scroll
bar.

The extension in Visual Studio 2026

For years my answer to that has been code-behind: move the C# into a partial class in a Component.razor.cs file, and let the .razor file go back to being about markup. Visual Studio even had a handy little refactoring to do it for you.

Then I upgraded to Visual Studio 2026 and that feature was gone.

So I did what any of us eventually do when a tool stops doing the thing we like: I built a small extension to bring it back. In this article I want to show you what it does, why I wanted it in the first place, and the one genuinely interesting problem I ran into while building it.

Why I wanted code-behind in the first place

Let me be clear up front: there is nothing wrong with keeping @code inside the .razor file. It is idiomatic Blazor, and for a lot of components it is the right call. This isn’t a “you’re doing it wrong” post.

But for the components that carry real logic, I like code-behind for a few practical reasons:

  • Separation of concerns. The .razor file becomes markup and binding. The .cs file becomes
    behaviour. When I open one of them I know what I’m there to look at.
  • A calmer diff. When markup and logic live in separate files, a change to one doesn’t muddy
    the history of the other. Reviews get easier.
  • Better tooling on the C#. In a plain .cs file, the full weight of the C# editor,
    analyzers, and refactorings is right there, without the Razor layer in between.
  • It scales. A 300-line single-file component is a chore to navigate. Split it, and each half
    is a comfortable size again.

Blazor supports this out of the box — the class generated from your .razor is already partial, so a Component.razor.cs with public partial class Component just works. The only friction was the manual busywork of moving the code across. That’s exactly the busywork the old Visual Studio feature removed… and exactly what I missed.

The extension: right-click, extract, done

The result is a small extension called Blazor Code-Behind Generator. The whole interaction is one menu item:

  1. In Solution Explorer, right-click a .razor file.
  2. Choose Extract Blazor Code-Behind.

That’s it. The extension:

  • Finds the component’s @code { … } block (it also understands the older @functions { … }).
  • Creates Component.razor.cs as a public partial class, with the correct namespace worked
    out the way Blazor does it — your root namespace plus the folder path — so it matches the class
    the Razor compiler generates.
  • Moves your fields and methods across, carries over the component’s @using directives, and adds
    the usual using statements.
  • Removes the @code from the .razor file, editing the open editor buffer directly so Visual
    Studio doesn’t nag you with a “this file changed on disk” prompt.

The command only shows up on .razor files, so it stays out of your way everywhere else.

Note on the built-in “Add Component.razor.cs”. Visual Studio still has a menu item that creates a code-behind file — but it gives you an empty partial class. It doesn’t move your @code into it. That’s the gap this extension fills: it actually does the extraction.

The interesting part: not all @code can move

Here’s where it got fun. I expected this to be a “read the block, write a file” afternoon project. And for most components, it is. But Blazor has a feature that makes the general case genuinely tricky, and it’s worth understanding even if you never touch the extension.

Razor lets you write inline markup templates inside your C#. If you’ve ever built a data grid with templated columns, you’ve written something like this:

private void BuildColumns()
{
    _columns =
    [
        new() { Title = "Status", Template = job => @<span class="badge">@job.Status</span> },
        // ...
    ];
}

Look at that @<span>…</span>. That @< is a Razor markup transition — a little doorway from C# back into markup, producing a RenderFragment<T>. It is enormously convenient.

It is also not valid C#.

The Razor compiler understands @<…> and rewrites it into render-tree building code. But a plain .cs file has no Razor compiler in front of it. So if you naively move a method containing @<span> into a code-behind, it simply won’t compile. This isn’t a limitation of my extension — it’s a hard rule of how Blazor works. Inline markup templates must live in a .razor file.

I learned this the honest way: my first version happily moved such a block into a .cs file, and the project lit up with red squiggles.

The fix: partial extraction

I had a choice. I could detect the problem and refuse the whole thing — safe, but not very helpful. Or I could be smarter about it.

The extension now does partial extraction. When it opens the @code block, it splits it into individual members — each field, each method, each property — and checks each one:

  • Members that are plain C# → moved to the code-behind.
  • Members that contain inline markup (@<…>) → left behind in a small residual @code block
    in the .razor file.

Because a Blazor component is one partial class, the two halves can reference each other freely — a method that stayed in the .razor can still call a method that moved to the .cs, and vice versa. Afterwards, the extension tells you exactly which members it kept behind and why.

In practice this is a huge win. Take a real dashboard component of mine: a dozen fields, several async loading and auto-refresh methods, filter builders, event handlers — and one BuildColumns method full of templated @<…> columns. With one right-click, everything except BuildColumns moves to the code-behind. The one method that genuinely has to stay in the .razor stays, and the other ~90% of the code gets the clean split I wanted.

Doing that split correctly meant writing a small parser that walks the block tracking (), [] and {} nesting while skipping strings and comments — so a collection initializer like [ new() { … } ], an object initializer, an auto-property with an initializer, or a stray brace inside a string literal never trips it up. That was the meatiest and most satisfying part of the build.

What if I want everything in the code-behind?

Sometimes you do — you’d rather the templated members move too. The trick is to get the markup out of the C# entirely, and the idiomatic way to do that is with child components.

Take that status column. Instead of an inline template, extract the markup into a tiny component:

@* JobStatusBadge.razor *@
<span class="badge @CssFor(Status)">@Status</span>

@code {
    [Parameter, EditorRequired] public string Status { get; set; } = "";
    private static string CssFor(string s) => s switch { "Completed" => "ok", _ => "idle" };
}

Now the parent only needs to reference that component. Keep a small @code block in the .razor that defines just the templates:

@code {
    private RenderFragment<Job> _statusTemplate = default!;

    private void BuildCellTemplates()
    {
        _statusTemplate = job => @<JobStatusBadge Status="job.Status" />;
    }
}

…and BuildColumns — now pure C# — moves happily into the code-behind, referencing _statusTemplate by field. The markup lives in components, the logic lives in the code-behind, and only a couple of lines of template glue stay in the .razor. The extension gets you most of the way there automatically; this last step is a quick manual refactor for the components where you want it.

Getting the extension

The extension targets Visual Studio 2022 (17.14+) and Visual Studio 2026.

It’s MIT-licensed, so feel free to read the code, open an issue, or send a pull request.

Wrapping up

This started as a small itch — a feature I missed after an upgrade — and turned into a nice little lesson about how Blazor really compiles your components. The @<…> markup transition is one of those conveniences you use constantly without thinking about what it is; building a tool that has to move code across the C#/Razor boundary makes you appreciate exactly where that boundary lives.

If you like your Blazor components split the way I do, I hope it saves you the same busywork it saves me. And if you keep everything in one file — no judgement. This one’s just for the rest of us.

Happy coding!

Related posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.